home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / fmat_pas.arc / FMAT.PAS
Encoding:
Pascal/Delphi Source File  |  1985-11-10  |  16.0 KB  |  419 lines

  1. { Demonstrates formatting a floppy disk from Turbo Pascal.
  2.   This version only formats DSDD 9 sector floppies (360KB).
  3.   Works with DOS 2.0 or 3.0 in either 360K or 1.2M drives.
  4.   Does not support a /S option.
  5.   Supports a /N or -N option that turns off the verify after format.
  6.   Turn off verify step and obtain a 2x speedup vs. DOS format.
  7.  
  8.   Requires Turbo version 3.0 to compile. Compile with Minheap=Maxheap=$200.
  9.   Requires a cloning procedure after being compiled to a .COM file.
  10.   The cloning procedure copies the boot sector of an already-formatted
  11.   floppy into the program, where it can be used thereafter. To clone,
  12.   call the program as follows:
  13.  
  14.   FMAT @ <enter>
  15.  
  16.   Written 10/26/85. Kim Kokkonen, TurboPower Software.
  17.   Compuserve 72457,2131. (408)-378-3672.                                      }
  18.  
  19. PROGRAM Fmat;
  20.   TYPE
  21.     SectorBuffer = ARRAY[1..512] OF Char; { holds sector data during sector setup after formatting }
  22.     FakeSectorBuffer = RECORD { same size as sector Buffer but easily initialized in code segment }
  23.                          L, H : STRING[255]
  24.                        END;
  25.     FormatRecord = RECORD
  26.                      Cyl, Hed, Rec, Num : Byte
  27.                    END;
  28.     FormatArray = ARRAY[1..18] OF FormatRecord;
  29.     DiskBaseRec = RECORD
  30.                     Unk1, Unk2, Mtr, Bps, Eot, Gpl, Dtl, Glf, Fbf, Hst, Mst : Byte
  31.                   END;
  32.     DiskBasePtr = ^DiskBaseRec;
  33.     Registers = RECORD
  34.                   CASE Integer OF
  35.                     1 : (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : Integer);
  36.                     2 : (AL, AH, BL, BH, CL, CH, DL, DH : Byte)
  37.                 END;
  38.     FATtable = ARRAY[0..1023] OF Byte;
  39.  
  40.   CONST
  41.     { bootrecord is customized after the .COM file is created.
  42.     call FMAT with a single command line parameter '@' as follows:
  43.     FMAT @ <Enter>. This will fill in bootrecord with a real boot record }
  44.     BootRecord : FakeSectorBuffer = { fill in with bootrecord } (l : ''; H : '');
  45.  
  46.   VAR
  47.     Reg : Registers;
  48.     SB : SectorBuffer;        { will fill in with dir sectors }
  49.     BR : SectorBuffer ABSOLUTE BootRecord;
  50.     VB : ARRAY[1..9] OF SectorBuffer; { will use for fast verify }
  51.     FMT : FormatArray;
  52.     FAT : FATtable;
  53.     DrName : STRING[2];
  54.     Param : STRING[10];
  55.     Drive, D_Type : Byte;
  56.     Ch : Char;
  57.     DoVerify : Boolean;
  58.     BiosDiskBase : DiskBasePtr ABSOLUTE 0 : $78;
  59.     OldDiskBase : DiskBasePtr;
  60.     I, Error : Integer;
  61.     T_Avail : Integer;
  62.     B_Avail : Real;
  63.  
  64.   PROCEDURE BiosReadSectors (Funct, Drive : Byte;
  65.                              Sector, Track, Head : Integer;
  66.                              Sects : Integer;
  67.                              VAR Buffer;
  68.                              VAR Error : Integer);
  69.       { -execute int 13 to read disk or verify via BIOS at low level }
  70.     BEGIN
  71.       Reg.AX := (Funct Shl 8) OR Sects;
  72.       Reg.DL := Drive;
  73.       Reg.DH := Head;
  74.       Reg.CH := Track AND 255;
  75.       Reg.CL := (Sector AND 63) OR ((Track Shr 8) Shl 6);
  76.       Reg.ES := Seg (Buffer);
  77.       Reg.BX := Ofs (Buffer);
  78.       Intr ($13, Reg);
  79.       IF Odd (Reg.Flags AND 1) THEN
  80.         Error := Reg.AX Shr 8
  81.       ELSE
  82.         Error := 0
  83.     END; { BiosReadSectors }
  84.  
  85.   PROCEDURE BIOS_WriteSectors (Drive : Byte;
  86.                              Sector, Track, Head : Integer;
  87.                              Sects : Integer;
  88.                              VAR Buffer : SectorBuffer;
  89.                              VAR Error : Integer);
  90.       { -execute int 13 to write disk via BIOS at low level }
  91.     BEGIN
  92.       Reg.AX := $300 OR Sects;
  93.       Reg.DL := Drive;
  94.       Reg.DH := Head;
  95.       Reg.CH := Track AND 255;
  96.       Reg.CL := (Sector AND 63) OR ((Track Shr 8) Shl 6);
  97.       Reg.ES := Seg (Buffer);
  98.       Reg.BX := Ofs (Buffer);
  99.       Intr ($13, Reg);
  100.       IF Odd (Reg.Flags AND 1) THEN
  101.         BEGIN
  102.           Error := Reg.AX Shr 8;
  103.           WriteLn ('Error during format...');
  104.           Halt;
  105.         END
  106.       ELSE
  107.         Error := 0
  108.     END; { BIOS_WriteSectors }
  109.  
  110.   PROCEDURE InitBoot;
  111.       { -self-customize this program to hold the boot record }
  112.     VAR
  113.       Ch : Char;
  114.       Error : Integer;
  115.       F : FILE;
  116.       Tries : Byte;
  117.  
  118.     FUNCTION CodeSize : Integer; { thanks to B. Tolz and R. Forgaard for this function }
  119.       VAR
  120.         I : Byte;
  121.       BEGIN
  122.         I := 11;
  123.         WHILE NOT ((Mem[DSeg - 2:I+3] <> $00E9) AND
  124.                    (MemW[DSeg - 2:I+4] = $0000)) AND
  125.               NOT ((MemW[DSeg - 2:I+0] = $00E9) AND
  126.                    (MemW[DSeg - 2:I+2] = $E800)) DO
  127.           I := I+1;
  128.         CodeSize := ((((DSeg - 2) - CSeg) Shl 4) + I + 6) - $100
  129.       END; { CodeSize }
  130.  
  131.     BEGIN
  132.       WriteLn ('You will now clone a copy of the boot record into this program...');
  133.       WriteLn ('The completed version will be written to FMAT.COM');
  134.       Write ('Place a DOS formatted disk in Drive A: and press any key when ready ');
  135.       Read (KBD, Ch);
  136.       WriteLn;
  137.       Tries := 0;
  138.       REPEAT            { read the boot record }
  139.         Tries := Succ (Tries);
  140.         BiosReadSectors (2, 0, 1, 0, 0, 1, BR, Error);
  141.       UNTIL (Error = 0) OR (Tries = 3);
  142.       IF Error <> 0 THEN
  143.         BEGIN
  144.           WriteLn ('Could not read boot record.');
  145.           Halt
  146.         END;
  147.       Assign (F, 'FMAT.COM'); { clone this program }
  148.       Rewrite (F, 1);
  149.       BlockWrite (F, Mem[CSeg : $100], CodeSize);
  150.       Close (F);
  151.       Halt
  152.     END; { InitBoot }
  153.  
  154.   FUNCTION DOS_Version : Byte; { -return the major version number of DOS }
  155.     BEGIN
  156.       Reg.AH := $30;
  157.       MsDos (Reg);
  158.       DOS_Version := Reg.AL
  159.     END; { DOS_Version }
  160.  
  161.   FUNCTION ATmachine : Boolean; { -return true if machine is AT class }
  162.     VAR
  163.       MachType : Byte ABSOLUTE $FFFF:$000E;
  164.     BEGIN
  165.       ATmachine := (MachType = $FC)
  166.     END; { ATmachine }
  167.  
  168.   PROCEDURE ReadDASD (Drive : Byte; VAR D_Type : Byte); { -read dasd for DOS 3 }
  169.     BEGIN
  170.       Reg.AH := $15;
  171.       Reg.DL := Drive;
  172.       Intr ($13, Reg);
  173.       IF Odd (Reg.Flags AND 1) THEN
  174.         BEGIN
  175.           WriteLn ('Error reading DASD for format...');
  176.           Halt
  177.         END;
  178.       D_Type := Reg.AH
  179.     END; { ReadDASD }
  180.  
  181.   PROCEDURE SetDASD (Drive, D_Type : Byte); { -execute int 13 to "set DASD" for format of 360K disks on 1.2MB floppies }
  182.     VAR
  183.       Tries : Byte;
  184.     BEGIN
  185.       Tries := 0;
  186.       REPEAT
  187.         Tries := Succ (Tries);
  188.         Reg.AH := $17;
  189.         Reg.AL := D_Type;
  190.         Reg.DL := Drive;
  191.         Intr($13, Reg);
  192.       UNTIL (Tries = 3) OR NOT (Odd (Reg.Flags AND 1));
  193.       IF Odd (Reg.Flags AND 1) THEN
  194.         BEGIN
  195.           WriteLn ('Error setting DASD for format...');
  196.           Halt
  197.         END
  198.     END; { SetDASD }
  199.  
  200.   PROCEDURE Init_FAT; { -initialize a FAT sector }
  201.     BEGIN { fill fat with all zeros }
  202.       FillChar (FAT, 1024, 0); { fill in the ID Bytes }
  203.       FAT[0] := $FD;           { 9 sector DSDD Drive }
  204.       FAT[1] := $FF;           { boilerplate }
  205.       FAT[2] := $FF;
  206.       T_Avail := 80
  207.     END; { Init_FAT }
  208.  
  209.   PROCEDURE InitDiskBase; { -modify the disk base data per DOS 3 instructions }
  210.     BEGIN
  211.       OldDiskBase := BiosDiskBase;   { save old pointer }
  212.       New (BiosDiskBase);            { make a new disk base data area }
  213.       BiosDiskBase^ := OldDiskBase^; { put the data from the old area in the new one }
  214.       BiosDiskBase^.Glf := $50;      { modify per dos 3 instructions, doesn'T hurt on DOS 2 }
  215.       BiosDiskBase^.Eot := 9;
  216.     END { InitDiskBase } ;
  217.  
  218.   PROCEDURE Format (Drive : Byte; VAR FMT : FormatArray; VAR Error : Integer);
  219.     VAR
  220.       I : Integer;
  221.     BEGIN
  222.       FOR I := 1 TO 9 DO        { initialize format table }
  223.         WITH FMT[I] DO
  224.           BEGIN
  225.             Cyl := 0;           { cylinder number, will fill in during format }
  226.             Hed := 0;           { Head number }
  227.             Rec := I;           { sector number }
  228.             Num := 2            { indicates 512 bytes per sector }
  229.           END;
  230.       FOR I := 1 TO 9 DO
  231.         WITH FMT[I+9] DO
  232.           BEGIN
  233.             Cyl := 0;           { cylinder number, will fill in during format }
  234.             Hed := 1;           { Head number }
  235.             Rec := I;           { sector number }
  236.             Num := 2            { indicates 512 bytes per sector }
  237.           END;
  238.       { write the format information }
  239.       InLine ($8A/$56/$0C/      { MOV    DL,[BP+0C] - get Drive number }
  240.               $C4/$5E/$08/      { LES    BX,[BP+08] - get pointer to format array }
  241.               $B9/$01/$00/      { MOV    CX,0001 - track 0 sector 1 }
  242.         { nexttrack: - loop over 40 disk tracks }
  243.               $8B/$FB/          { MOV    DI,BX - index into format array }
  244.               $B0/$12/          { MOV    AL,12 - number of sectors per track = 18 }
  245.         { inittrack: - loop over 18 sectors per track }
  246.               $26/$88/$2D/      { MOV    ES:[DI],CH - track track number in format array }
  247.               $81/$C7/$04/$00/  { ADD    DI,0004 }
  248.               $FE/$C8/          { DEC    AL }
  249.               $75/$F5/          { JNZ    inittrack }
  250.               $B6/$00/          { MOV    DH,00 - format 9 sectors on side 0 }
  251.               $B8/$01/$05/      { MOV    AX,0501 }
  252.               $CD/$13/          { INT    13 }
  253.               $72/$18/          { JB     Error - check for errors }
  254.               $B6/$01/          { MOV    DH,01 - format 9 sectors on side 1 }
  255.               $B8/$01/$05/      { MOV    AX,0501 }
  256.               $53/              { PUSH    BX }
  257.               $81/$C3/$24/$00/  { ADD    BX,0024 }
  258.               $CD/$13/          { INT    13 }
  259.               $72/$0A/          { JB     Error - check for errors }
  260.               $5B/              { POP    BX }
  261.               $FE/$C5/          { INC    CH - next track }
  262.               $80/$FD/$28/      { CMP    CH,28 }
  263.               $75/$D2/          { JNZ    nexttrack }
  264.               $31/$C0/          { XOR    AX,AX - no errors, return 0 }
  265.         { Error: }
  266.               $C4/$7E/$04/      { LES    DI,[BP+04] }
  267.               $26/$89/$05);     { MOV    ES:[DI],AX - return Error code }
  268.     END; { Format }
  269.  
  270.   PROCEDURE Verify (Drive : Byte; VAR FAT : FATtable; VAR Error : Integer);
  271.     VAR
  272.       T, H : Integer;
  273.       Cluster, FAT_Ofs, TopCluster, Content : Integer;
  274.     BEGIN { initialize the verify Buffer - 9 sectors * 512 bytes }
  275.       FillChar (VB, 4608, $F6);
  276.       FOR T := 0 TO 39 DO { verify all sectors }
  277.         FOR H := 0 TO 1 DO
  278.           BEGIN
  279.             BiosReadSectors (4, Drive, 1, T, H, 9, VB, Error);
  280.             IF Error <> 0 THEN
  281.               BEGIN { mark the clusters on this track as unavailable }
  282.                 Cluster := ((9 * (H + 2 * T)) DIV 2) - 4;
  283.                 TopCluster := Cluster + 5;
  284.                 WHILE Cluster < TopCluster DO
  285.                   BEGIN
  286.                     FAT_Ofs := (3 * Cluster) DIV 2;
  287.                     Move (FAT[FAT_Ofs], Content, 2); { get a word from the FAT }
  288.                     IF Odd (Cluster) THEN            { replace 12 bits of the word }
  289.                       Content := Content OR $FF70
  290.                     ELSE
  291.                       Content := Content OR $0FF7;
  292.                     Move (Content, FAT[FAT_Ofs], 2);  { store it back }
  293.                     Cluster := Succ (Cluster)
  294.                   END;
  295.  
  296.                 T_Avail := Pred (T_Avail) { reduce the number of tracks available }
  297.               END
  298.           END
  299.     END; { Verify }
  300.  
  301.   PROCEDURE Init_DIR; { -initialize a sector for the root directory }
  302.     VAR
  303.       I : Integer;
  304.     BEGIN
  305.       FillChar (SB, 512, $F6); { fill with format bytes }
  306.       FOR I := 1 TO 481 DO     { mark each directory entry as available }
  307.         IF ((I - 1) MOD 32) = 0 THEN
  308.           SB[I] := #0
  309.     END; { Init_DIR }
  310.  
  311.   BEGIN
  312.     DoVerify := True;
  313.     IF ParamCount = 0 THEN     { get the Drive and DoVerify option }
  314.       BEGIN
  315.         Write ('Enter Drive to format: ');
  316.         ReadLn (DrName)
  317.       END
  318.     ELSE                       { read the command line parameters }
  319.       BEGIN
  320.         I := 1;
  321.         DrName := '';
  322.         WHILE I <= ParamCount DO
  323.           BEGIN
  324.             Param := ParamStr (I);
  325.             CASE Param[1] OF
  326.               '@'      : InitBoot;  { clone the boot record into this program }
  327.               '-', '/' : IF (Length (Param) = 2) AND (UpCase (Param[2]) = 'N') THEN
  328.                            DoVerify := False
  329.                          ELSE
  330.                            WriteLn ('WARNING: unrecognized command line option ', Param)
  331.             ELSE
  332.               DrName := Param
  333.             END;
  334.             I := Succ (I)
  335.           END
  336.       END;
  337.     IF BR[1] = #0 THEN  { make sure the bootrecord has been cloned into program }
  338.       BEGIN
  339.         WriteLn ('You must first clone a copy of the boot record');
  340.         WriteLn ('into this program. Call as FMAT @ <Enter> to clone...');
  341.         Halt
  342.       END;
  343.     IF (DrName = '') OR NOT (UpCase (DrName[1]) IN ['A', 'B']) THEN
  344.       BEGIN { check for errors, should use DOS facilities to check non-removables }
  345.         WriteLn ('Drive not Specified or cannot be formatted');
  346.         Halt
  347.       END;
  348.     REPEAT { get BIOS Drive number }
  349.       Drive := Ord (UpCase (DrName[1])) - 65;
  350.       Write ('Insert new disk in Drive ', Chr (65 + Drive));
  351.       Write (' and press <Enter> to begin formatting ');
  352.       REPEAT
  353.         Read (KBD, Ch)
  354.       UNTIL (Ch = ^M);
  355.       WriteLn;
  356.       IF ATmachine AND (DOS_Version = 3) THEN
  357.         BEGIN { get the Drive type, necessary when dealing with 1.2MB drives }
  358.           ReadDASD (Drive, D_Type);
  359.           IF (D_Type = 0) OR (D_Type = 3) THEN
  360.             BEGIN
  361.               WriteLn ('Drive is not present or non-removable');
  362.               Halt
  363.             END;
  364.           IF D_Type = 2 THEN
  365.             WriteLn ('Formatting 360K floppy in 1.2MB Drive');
  366.           SetDASD(Drive, D_Type) { set the DASD type accordingly }
  367.         END;
  368.       Write ('Formatting... ');
  369.       InitDiskBase; { set up the disk_base table }
  370.       Format (Drive, FMT, Error); { lay down format tracks }
  371.       BiosDiskBase := OldDiskBase; { restore the disk_base table }
  372.       IF Error <> 0 THEN
  373.         BEGIN
  374.           WriteLn ('Error during format...');
  375.           Halt
  376.         END;
  377.       Init_FAT; { initialize the FATtable }
  378.       IF DoVerify THEN
  379.         BEGIN   { verify sectors }
  380.           Write ('Verifying... ');
  381.           Verify (Drive, FAT, Error)
  382.         END;
  383.       IF Error <> 0 THEN
  384.         WriteLn ('Bad disk, format not verified...')
  385.       ELSE
  386.         BEGIN
  387.           Write ('Writing BOOT/FAT/DIR... ');
  388.           BIOS_WriteSectors(Drive, 1, 0, 0, 1, BR, Error); { write the boot record }
  389.           Move (FAT[0], SB, 512); { write the FAT sectors }
  390.           BIOS_WriteSectors (Drive, 2, 0, 0, 1, SB, Error);
  391.           Move (FAT[512], SB, 512);
  392.           BIOS_WriteSectors (Drive, 3, 0, 0, 1, SB, Error);
  393.           Move (FAT[0], SB, 512);
  394.           BIOS_WriteSectors (Drive, 4, 0, 0, 1, SB, Error);
  395.           Move (FAT[512], SB, 512);
  396.           BIOS_WriteSectors (Drive, 5, 0, 0, 1, SB, Error);
  397.           Init_DIR; { write the root directory }
  398.           BIOS_WriteSectors (Drive, 6, 0, 0, 1, SB, Error);
  399.           BIOS_WriteSectors (Drive, 7, 0, 0, 1, SB, Error);
  400.           BIOS_WriteSectors (Drive, 8, 0, 0, 1, SB, Error);
  401.           BIOS_WriteSectors (Drive, 9, 0, 0, 1, SB, Error);
  402.           BIOS_WriteSectors (Drive, 1, 0, 1, 1, SB, Error);
  403.           BIOS_WriteSectors (Drive, 2, 0, 1, 1, SB, Error);
  404.           BIOS_WriteSectors (Drive, 3, 0, 1, 1, SB, Error);
  405.           B_Avail := 512.0 * (9.0 * T_Avail - 12.0); { calculate bytes available on disk }
  406.                                                      { 12 sectors are used by BOOT/FAT/DIR }
  407.           WriteLn ('Format complete'#7);
  408.           WriteLn ('Bytes Available: ', B_Avail:0:0)
  409.         END;
  410.       WriteLn;
  411.       Write ('Format another? (Y/N) ');
  412.       REPEAT
  413.         Read (KBD, Ch);
  414.         Ch := UpCase (Ch)
  415.       UNTIL (Ch IN ['Y', 'N']);
  416.       WriteLn (Ch);
  417.     UNTIL Ch = 'N'
  418.   END.
  419.